package com.zhhtech.zhh.api.core

import androidx.lifecycle.*
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import java.io.Closeable
import java.io.IOException
import kotlin.coroutines.CoroutineContext

typealias ApiError = ((Throwable) -> Unit)
typealias ApiComplete = () -> Unit

fun <T> ResultBean<T>.toData(): T? =
    if (isOk) (data) else throw FirmException(msg, code, data)

/**
 * 统一数据封装类
 */
data class ResultBean<T>(
    private var code: Int = SUCCESS,
    var msg: String? = null,
    var data: T? = null,
) : com.zhhtech.zhh.api.core.base.IResult<T> {
    companion object {
        const val SUCCESS = 0
        const val CLOUD_SUCCESS = 2000
        const val SERVICE_ERROR = 500

        //未登录或登录失效
        const val NOT_LOGIN = 401
    }

    override fun isOk() = code == SUCCESS || code == CLOUD_SUCCESS

    override fun getTData() = data
    override fun getErrorMsg() = msg
    override fun getCode() = code
}

data class Page<T>(
    val list: MutableList<T> = mutableListOf(),
    val lastId: String = "",
)

/**
 * 业务异常类
 */
class FirmException(
    override var message: String? = "",
    val code: Int = ResultBean.SERVICE_ERROR,
    val data: Any? = null,
) : RuntimeException(message)

/**
 * 在OKHTTP拦截器里面只能抛该异常否则协程会无法捕获异常导致程序崩溃
 */
class InterceptorException(override var message: String? = "") : IOException(message)

/**
 * 自定义网络常类
 */
class NotNetworkException : RuntimeException("")

fun <T> LifecycleOwner.launchUI(
    block: suspend CoroutineScope.() -> T,
    apiObserver: Observer<ApiProcess<T>>? = null,
    error: ApiError? = null,
    complete: ApiComplete? = null,
): Job = ApiRequest(lifecycleScope, block, error, complete, apiObserver).launchUI()

fun <T> ViewModel.launchUI(
    block: suspend CoroutineScope.() -> T,
    apiObserver: Observer<ApiProcess<T>>? = null,
    error: ApiError? = null,
    complete: ApiComplete? = null,
): Job = ApiRequest(viewModelScope, block, error, complete, apiObserver).launchUI()

fun <T> globalLaunchUI(
    block: suspend CoroutineScope.() -> T,
    apiObserver: Observer<ApiProcess<T>>? = null,
    error: ApiError? = null,
    complete: ApiComplete? = null,
): Job = ApiRequest(uiScope(), block, error, complete, apiObserver).launchUI()

/**
 * 使用类的方式调用方便以后实现重试机制
 * 调用方式
 * launchUI({网络请求},ApiObserver())
val test: ApiLiveData<String> = ApiLiveData()
 * launchUI({网络请求},test)
 */
class ApiRequest<T>(
    private val scope: CoroutineScope,
    private var block: (suspend CoroutineScope.() -> T)? = null,
    private var error: ApiError? = null,
    private var complete: ApiComplete? = null,
    private var apiObserver: Observer<ApiProcess<T>>? = null,
) {
    companion object {
        private const val RETRY_DELAY_MILLIS = 2000
        private const val MAX_RETRIES = 3

        private val mutex = Mutex()
    }

    private val maxRetries: Int = MAX_RETRIES
    private val retryDelayMillis: Int = RETRY_DELAY_MILLIS

    fun launchUI(): Job {
        apiObserver?.onChanged(ApiProcess())
        return scope.launch(CoroutineExceptionHandler { _, throwable ->
            error?.invoke(throwable)
            apiObserver?.onChanged(ApiProcess(apiCode = com.zhhtech.zhh.api.core.enums.ApiCode.ERROR, throwable = throwable))
            apiObserver?.onChanged(ApiProcess(apiCode = com.zhhtech.zhh.api.core.enums.ApiCode.COMPLETE))
        }) {
            try {
                val result = block?.invoke(this)
                apiObserver?.onChanged(ApiProcess(apiCode = com.zhhtech.zhh.api.core.enums.ApiCode.SUCCESS, data = result))
            } catch (throwable: Throwable) {
                try {
//                    if (throwable is FirmException && throwable.code == ResultBean.NOT_LOGIN) {
//                        val routerService = RouterServiceBootstrap.getService<IRouterService>()
//                        routerService.logout()
//                        val autoLogin = mutex.withLock {
//                            if (!routerService.isLogin()) {
//                                // 如果是接口请求完调用toData判断code抛的异常是可以被try到的，如果是非code判断异常抓不到直接往外抛
//                                routerService.autoLogin()
//                            } else {
//                                false
//                            }
//                        }
//                        if (autoLogin) {
//                            // 登录成功，再次执行之前的请求
//                            launchUI()
//                            return@launch
//                        }
//                    }
                    // 后续扩展实现重试机制
                    error?.invoke(throwable)
                    apiObserver?.onChanged(ApiProcess(apiCode = com.zhhtech.zhh.api.core.enums.ApiCode.ERROR, throwable = throwable))
                } catch (e1: Exception) {
                    apiObserver?.onChanged(ApiProcess(apiCode = com.zhhtech.zhh.api.core.enums.ApiCode.ERROR, throwable = e1))
                }
            }
            try {
                complete?.invoke()
                apiObserver?.onChanged(ApiProcess(com.zhhtech.zhh.api.core.enums.ApiCode.COMPLETE))
            } catch (e1: Exception) {
                apiObserver?.onChanged(ApiProcess(com.zhhtech.zhh.api.core.enums.ApiCode.COMPLETE))
            }
        }
    }
}

/**
 * 返回livedata方式请求接口；目前接口统一定义在ApiService；这种方式订阅者必须传递生命周期组件否则可能会内存泄漏
 * @param error 请求错误回调
 * @param complete 请求完成回调
 * @param block 请求代码块，最后一行的返回结果会传递到订阅者
 * 请求示例
 * apiLiveData { queryOrg("").toData() }.observe(this, ApiObserver(loadService = mLoadService) {
 *  TODO 自己的业务逻辑
 * })
 */
fun <T> apiLiveData(
    error: ApiError? = null,
    complete: ApiComplete? = null,
    block: suspend LiveDataScope<*>.() -> T,
) = liveData<ApiProcess<T>>(CoroutineExceptionHandler { _, throwable ->
    error?.invoke(throwable)
}) {
    kotlin.runCatching { emit(ApiProcess()) }
    try {
        val result: T = block()
        kotlin.runCatching { emit(ApiProcess(apiCode = com.zhhtech.zhh.api.core.enums.ApiCode.SUCCESS, data = result)) }
    } catch (throwable: Throwable) {
        kotlin.runCatching { emit(ApiProcess(apiCode = com.zhhtech.zhh.api.core.enums.ApiCode.ERROR, throwable = throwable)) }
    }
    kotlin.runCatching { complete?.invoke() }
    kotlin.runCatching { emit(ApiProcess(com.zhhtech.zhh.api.core.enums.ApiCode.COMPLETE)) }
}

/**
 * 在非viewmodel地方使用
 */
fun uiScope() = SafeCoroutineScope(Dispatchers.Main)

class SafeCoroutineScope(context: CoroutineContext) : CoroutineScope, Closeable {
    override val coroutineContext: CoroutineContext = SupervisorJob() + context

    override fun close() {
        coroutineContext.cancelChildren()
    }
}